module spi_slave_wrapper
(
	clock,
	resetn,
	writedata,
	read,
	write,
	chip_select,
	address,

	readdata,
	wait_request,

	spi_clk,
	spi_cs,
	spi_miso,
	spi_mosi,

	slave_irq,

	irq,
	chip_enable
);

// port definition
input clock, resetn, read, write, chip_select;
input [7:0] writedata;
input [2:0] address;
input spi_miso;
input slave_irq;

output wait_request;
output [7:0] readdata;
output spi_clk, spi_mosi, spi_cs;
output chip_enable;
output irq;

// state variables
integer IDLE = 0, WORKING = 1;
reg [2:0] state, next_state;

// variables
reg [7:0] read_count;
wire read_count_load_enable;
wire fifo_rx_re,fifo_rx_we, fifo_rx_full, fifo_rx_empty;
wire fifo_tx_re,fifo_tx_we, fifo_tx_full, fifo_tx_empty;
wire [9:0] fifo_rx_count, fifo_tx_count;
wire [7:0] fifo_tx_readdata, fifo_rx_writedata, fifo_rx_readdata;
wire spi_interface_start, spi_interface_done;
wire write_mode, read_mode;
wire chip_enable_load;
reg chip_enable;

// state outputs
assign write_mode = (state == IDLE && chip_select && write && !read); 
assign read_mode = (state == IDLE && chip_select && read && !write); 
assign read_count_load_enable = write_mode && address == 3'd1;
assign fifo_tx_we = write_mode  && address == 3'd2 && fifo_tx_full != 1;
assign spi_interface_start = write_mode && address == 3'd0;
assign wait_request = (write_mode && address == 3'd0) || (state == WORKING && spi_interface_done == 0);
assign fifo_rx_re = read_mode && address == 3'd0 && fifo_rx_empty != 1;
assign readdata = (read_mode && address == 3'd1 ) ? fifo_rx_count[7:0] : (read_mode && address == 3'd2) ?fifo_tx_count[7:0] : (read_mode && address == 3'd3 ) ? {7'b0,chip_enable} : fifo_rx_readdata;
assign chip_enable_load = (write_mode && address == 3'd3);
assign irq=slave_irq;

// chip enable register
always @(posedge clock or negedge resetn)
begin
	if (resetn == 0)
		chip_enable <= 0;
	else if (chip_enable_load)
		chip_enable <= writedata[0];
end

// read count register
always @(posedge clock or negedge resetn)
begin
	if (resetn == 0)
		read_count <= 0;
	else if (read_count_load_enable)
		read_count <= writedata;
end

// spi interface module
spi_interface spi_interface_instance
(
        .clock(clock),
        .resetn(resetn),

        .fifo_tx_re(fifo_tx_re),
        .fifo_tx_empty(fifo_tx_empty),
        .fifo_tx_readdata(fifo_tx_readdata),

        .fifo_rx_we(fifo_rx_we),
        .fifo_rx_full(fifo_rx_full),
        .fifo_rx_writedata(fifo_rx_writedata),

        .spi_cs(spi_cs),
        .spi_clk(spi_clk),
        .spi_mosi(spi_mosi),
        .spi_miso(spi_miso),

        .start(spi_interface_start),
        .read_count(read_count),
        .done(spi_interface_done)
);

// fifo tx rx module
fifo spi_fifo_rx
(
	.clock(clock),
	.resetn(resetn),
	
	.writeenable(fifo_rx_we),
	.writedata(fifo_rx_writedata),
	.readenable(fifo_rx_re),
	.readdata(fifo_rx_readdata),
	
	.empty(fifo_rx_empty),
	.full(fifo_rx_full),

	.count(fifo_rx_count)
);

fifo spi_fifo_tx
(
	.clock(clock),
	.resetn(resetn),
	
	.writeenable(fifo_tx_we),
	.writedata(writedata),
	.readenable(fifo_tx_re),
	.readdata(fifo_tx_readdata),
	
	.empty(fifo_tx_empty),
	.full(fifo_tx_full),


	.count(fifo_tx_count)
);




// state machine
always @(posedge clock or negedge resetn)
begin
	if (resetn == 0)
		state <= IDLE;
	else
		state <= next_state;
end

always @(*)
begin
	case (state)
		IDLE:
			if (chip_select && write && address == 3'd0)
				next_state = WORKING;
			else
				next_state = IDLE;
		WORKING:
			if (spi_interface_done)
				next_state = IDLE;
			else
				next_state = WORKING;
		default:
			next_state = IDLE;
	endcase
end
endmodule
